﻿using System;
using System.Runtime.InteropServices;

using OmniKits.Core;

namespace OmniKits.Extensions
{
    public static class FloatLexicalConversionEx
    {
#pragma warning disable 1591

        const uint UIntMidPoint = 0x80000000;
        const int IntMidMask = (int)~UIntMidPoint;
        const ulong ULongMidPoint = UIntMidPoint * 0x100000000u;
        const long LongMidMask = (long)~ULongMidPoint;

        #region 64位

        #region 有符号整数

        public static long ToSortableInt64(this double value)
        {
            //long tmp = *(long*)(&value);
            //var tmp = new DoublePayload { Double = value }.Int64;
            //var tmp = new Unchecked<double, long> { ValueA = value }.ValueB;

            var tmp = BitConverter.DoubleToInt64Bits(value);

            if (tmp < 0)
            {
                tmp ^= LongMidMask;
            }

            return tmp;
        }

        public static double ToSortableDouble(this long value)
        {
            if (value < 0)
            {
                value ^= LongMidMask;
            }

            //return new DoublePayload { Int64 = value }.Double;
            //return new Unchecked<long, double> { ValueA = value }.ValueB;
            //return *(double*)&value;

            return BitConverter.Int64BitsToDouble(value);
        }

        #endregion

#if IncludeNonClsCompliantElements
        #region 无符号整数

        [StructLayout(LayoutKind.Explicit)]
        private struct DoubleUInt64Union
        {
            [FieldOffset(0)]
            public double Double;
            [FieldOffset(0)]
            public UInt64 UInt64;
        }

        [CLSCompliant(false)]
        public static ulong ToSortableUInt64(this double value)
        {
            //ulong tmp = *(ulong*)(&value);
            //var tmp = new DoublePayload { Double = value }.UInt64;
            var tmp = new DoubleUInt64Union { Double = value }.UInt64;

            if (tmp < ULongMidPoint)
            {
                tmp ^= ULongMidPoint;
            }
            else
            {
                tmp = ~tmp;
            }

            return tmp;
        }

        [CLSCompliant(false)]
        public static double ToSortableDouble(this ulong value)
        {
            if (value < ULongMidPoint)
            {
                value = ~value;
            }
            else
            {
                value ^= ULongMidPoint;
            }

            //return new DoublePayload { UInt64 = value }.Double;
            return new DoubleUInt64Union { UInt64 = value }.Double;

            //return *(double*)(&value);
        }

        #endregion
#endif

        #endregion

        #region 32位

        #region 有符号整数

        [StructLayout(LayoutKind.Explicit)]
        private struct SingleInt32Union
        {
            [FieldOffset(0)]
            public Single Single;
            [FieldOffset(0)]
            public Int32 Int32;
        }

        public static int ToSortableInt(this float value)
        {
            //int tmp = *(int*)(&value);

            //var tmp = new SinglePayload { Single = value }.Int32;
            var tmp = new SingleInt32Union { Single = value }.Int32;

            if (tmp < 0)
            {
                tmp ^= IntMidMask;
            }

            return tmp;
        }

        public static float ToSortableSingle(this int value)
        {
            if (value < 0)
            {
                value ^= IntMidMask;
            }

            //return new SinglePayload { Int32 = value }.Single;
            return new SingleInt32Union { Int32 = value }.Single;

            //return *(float*)&value;
        }

        #endregion

#if IncludeNonClsCompliantElements
        #region 无符号整数

        [StructLayout(LayoutKind.Explicit)]
        private struct SingleUInt32Union
        {
            [FieldOffset(0)]
            public Single Single;
            [FieldOffset(0)]
            public UInt32 UInt32;
        }

        [CLSCompliant(false)]
        public static uint ToSortableUInt(this float value)
        {
            //uint tmp = *(uint*)(&value);

            //var tmp = new SinglePayload { Single = value }.UInt32;
            var tmp = new SingleUInt32Union { Single = value }.UInt32;

            if (tmp < UIntMidPoint)
            {
                tmp ^= UIntMidPoint;
            }
            else
            {
                tmp = ~tmp;
            }

            return tmp;
        }

        [CLSCompliant(false)]
        public static float ToSortableSingle(this uint value)
        {
            if (value < UIntMidPoint)
            {
                value = ~value;
            }
            else
            {
                value ^= UIntMidPoint;
            }

            //return new SinglePayload { UInt32 = value }.Single;
            return new SingleUInt32Union { UInt32 = value }.Single;

            //return *(float*)(&value);
        }
        #endregion
#endif

        #endregion

    }
}
